#include "wait_exit.h"
#include "stdint.h"
#include "global.h"
#include "thread.h"
#include "fs.h"
#include "debug.h"
#include "pipe.h"
#include "file.h"

/* 释放用户进程资源:
 * 1 页表中对应的物理页
 * 2 虚拟内存池占物理页框
 * 3 关闭打开的文件 */
static void release_prog_resource(struct task_struct *release_thread)
{
    uint32_t *pgdir_vaddr = release_thread->pgdir;
    uint16_t user_pde_nr = 768, pde_idx = 0;
    uint32_t pde = 0;
    uint32_t *v_pde_ptr = NULL; // v表示var,和函数pde_ptr区分

    uint16_t user_pte_nr = 1024, pte_idx = 0;
    uint32_t pte = 0;
    uint32_t *v_pte_ptr = NULL; // 加个v表示var,和函数pte_ptr区分

    uint32_t *first_pte_vaddr_in_pde = NULL; // 用来记录pde中第0个pte的地址
    uint32_t pg_phy_addr = 0;

    /* 回收页表中用户空间的页框 */
    while (pde_idx < user_pde_nr)
    {
        v_pde_ptr = pgdir_vaddr + pde_idx;
        pde = *v_pde_ptr;
        if (pde & 0x00000001)
        {                                                         // 如果页目录项p位为1,表示该页目录项下可能有页表项
            first_pte_vaddr_in_pde = pte_ptr(pde_idx * 0x400000); // 一个页表表示的内存容量是4M,即0x400000
            pte_idx = 0;
            while (pte_idx < user_pte_nr)
            {
                v_pte_ptr = first_pte_vaddr_in_pde + pte_idx;
                pte = *v_pte_ptr;
                if (pte & 0x00000001)
                {
                    /* 将pte中记录的物理页框直接在相应内存池的位图中清0 */
                    pg_phy_addr = pte & 0xfffff000;
                    free_a_phy_page(pg_phy_addr);
                }
                pte_idx++;
            }
            /* 将pde中记录的物理页框直接在相应内存池的位图中清0 */
            pg_phy_addr = pde & 0xfffff000;
            free_a_phy_page(pg_phy_addr);
        }
        pde_idx++;
    }

    /* 回收用户虚拟地址池所占的物理内存*/
    uint32_t bitmap_pg_cnt = (release_thread->userprog_vaddr.vaddr_bitmap.btmp_bytes_len) / PG_SIZE;
    uint8_t *user_vaddr_pool_bitmap = release_thread->userprog_vaddr.vaddr_bitmap.bits;
    mfree_page(PF_KERNEL, user_vaddr_pool_bitmap, bitmap_pg_cnt);

    /* 关闭进程打开的文件 */
    uint8_t local_fd = 3;
    while (local_fd < MAX_FILES_OPEN_PER_PROC)
    {
        if (release_thread->fd_table[local_fd] != -1)
        {
            if (is_pipe(local_fd))
            {
                uint32_t global_fd = fd_local2global(local_fd);
                if (--file_table[global_fd].fd_pos == 0)
                {
                    mfree_page(PF_KERNEL, file_table[global_fd].fd_inode, 1);
                    file_table[global_fd].fd_inode = NULL;
                }
            }
            else
            {
                sys_close(local_fd);
            }
        }
        local_fd++;
    }
}

/* list_traversal的回调函数,
 * 查找pelem的parent_pid是否是ppid,成功返回true,失败则返回false */
static bool find_child(struct list_elem *pelem, int32_t ppid)
{
    /* elem2entry中间的参数all_list_tag取决于pelem对应的变量名 */
    struct task_struct *pthread = elem2entry(struct task_struct, all_list_tag, pelem);
    if (pthread->parent_pid == ppid)
    {                // 若该任务的parent_pid为ppid,返回
        return true; // list_traversal只有在回调函数返回true时才会停止继续遍历,所以在此返回true
    }
    return false; // 让list_traversal继续传递下一个元素
}

/* list_traversal的回调函数,
 * 查找状态为TASK_HANGING的任务 */
static bool find_hanging_child(struct list_elem *pelem, int32_t ppid)
{
    struct task_struct *pthread = elem2entry(struct task_struct, all_list_tag, pelem);
    if (pthread->parent_pid == ppid && pthread->status == TASK_HANGING)
    {
        return true;
    }
    return false;
}

/* list_traversal的回调函数,
 * 将一个子进程过继给init */
static bool init_adopt_a_child(struct list_elem *pelem, int32_t pid)
{
    struct task_struct *pthread = elem2entry(struct task_struct, all_list_tag, pelem);
    if (pthread->parent_pid == pid)
    { // 若该进程的parent_pid为pid,返回
        pthread->parent_pid = 1;
    }
    return false; // 让list_traversal继续传递下一个元素
}

/* 等待子进程调用exit,将子进程的退出状态保存到status指向的变量.
 * 成功则返回子进程的pid,失败则返回-1 */
pid_t sys_wait(int32_t *status)
{
    struct task_struct *parent_thread = running_thread();

    while (1)
    {
        /* 优先处理已经是挂起状态的任务 */
        struct list_elem *child_elem = list_traversal(&thread_all_list, find_hanging_child, parent_thread->pid);
        /* 若有挂起的子进程 */
        if (child_elem != NULL)
        {
            struct task_struct *child_thread = elem2entry(struct task_struct, all_list_tag, child_elem);
            *status = child_thread->exit_status;

            /* thread_exit之后,pcb会被回收,因此提前获取pid */
            uint16_t child_pid = child_thread->pid;

            /* 2 从就绪队列和全部队列中删除进程表项*/
            thread_exit(child_thread, false); // 传入false,使thread_exit调用后回到此处
            /* 进程表项是进程或线程的最后保留的资源, 至此该进程彻底消失了 */

            return child_pid;
        }

        /* 判断是否有子进程 */
        child_elem = list_traversal(&thread_all_list, find_child, parent_thread->pid);
        if (child_elem == NULL)
        { // 若没有子进程则出错返回
            return -1;
        }
        else
        {
            /* 若子进程还未运行完,即还未调用exit,则将自己挂起,直到子进程在执行exit时将自己唤醒 */
            thread_block(TASK_WAITING);
        }
    }
}

/* 子进程用来结束自己时调用 */
void sys_exit(int32_t status)
{
    struct task_struct *child_thread = running_thread();
    child_thread->exit_status = status;
    if (child_thread->parent_pid == -1)
    {
        PANIC("sys_exit: child_thread->parent_pid is -1\n");
    }

    /* 将进程child_thread的所有子进程都过继给init */
    list_traversal(&thread_all_list, init_adopt_a_child, child_thread->pid);

    /* 回收进程child_thread的资源 */
    release_prog_resource(child_thread);

    /* 如果父进程正在等待子进程退出,将父进程唤醒 */
    struct task_struct *parent_thread = pid2thread(child_thread->parent_pid);
    if (parent_thread->status == TASK_WAITING)
    {
        thread_unblock(parent_thread);
    }

    /* 将自己挂起,等待父进程获取其status,并回收其pcb */
    thread_block(TASK_HANGING);
}